/*
 * 	simui.c
 *
 * 	Created on: August 14, 2011
 *      Authors: Lane Aasen, Nikholas Gaffney, Michael Rosenberger, Dylan Swiggett
 */

#include <gtk/gtk.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <glade/glade.h>

#define	WDT	800	/*width of the simuiW*/
#define HGT	500	/*height of ^^^*/	

/*gcc -Wall -g simui.c -o arcui `pkg-config --cflags gtk+-2.0` `pkg-config --libs gtk+-2.0` `pkg-config libglade-2.0 --cflags` `pkg-config libglade-2.0 --libs`*/

/*GTK must be notified before and after its resources are manipulated outside of gtk_main()*/
/*this is done by calling the functions gdk_threads_enter() and gdk_threads_leave()*/

/*global pixmap buffer*/
static GdkPixmap *pixmap = NULL;

gboolean win_config(GtkWidget* da, GdkEventConfigure* event, gpointer user_data) {
    static int oldw = 0;
    static int oldh = 0;
    /*resize pixmap if applicable*/
    if(oldw != event->width || oldh != event->height) {
     	/*create temporary pixmap*/
        GdkPixmap *tpixmap = gdk_pixmap_new(da->window, event->width,  event->height, -1);
        /*copy old pixmap to new pixmap*/
        int minw = oldw, minh = oldh;
        if(event->width < minw) minw =  event->width; 
        if(event->height < minh) minh =  event->height; 
        gdk_draw_drawable(tpixmap, da->style->fg_gc[GTK_WIDGET_STATE(da)], pixmap, 0, 0, 0, 0, minw, minh);
        /*replace old pixmap with new one*/
        g_object_unref(pixmap); 
        pixmap = tpixmap;
    }
    oldw = event->width;
    oldh = event->height;
    return TRUE;
}

gboolean win_expose(GtkWidget* da, GdkEventExpose* event, gpointer user_data) {
    gdk_draw_drawable(da->window,
        da->style->fg_gc[GTK_WIDGET_STATE(da)], pixmap,
        event->area.x, event->area.y,
        event->area.x, event->area.y,
        event->area.width, event->area.height);
    return TRUE;
}


static int currently_drawing = 0;
/*animation function which will be executed in a seperate thread*/
void *do_draw(void *ptr) {

    /*preparation to recieve SIGALRM*/
    siginfo_t info;
    sigset_t sigset;

    sigemptyset(&sigset);
    sigaddset(&sigset, SIGALRM);

    while(1) {
        /*wait for SIGALRM, then illustrate*/
        while(sigwaitinfo(&sigset, &info) > 0) {
            currently_drawing = 1;

            int width, height;
            gdk_threads_enter();
            gdk_drawable_get_size(pixmap, &width, &height);
            gdk_threads_leave();

            /*create cairo surface for illustration*/
            cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
            cairo_t *cr = cairo_create(cst);

			/*perform illustration*/
            
            /*draw the cairo surface onto the GTK pixmap*/
            gdk_threads_enter();
            cairo_t *cr_pixmap = gdk_cairo_create(pixmap);
            cairo_set_source_surface(cr_pixmap, cst, 0, 0);
            cairo_paint(cr_pixmap);
            cairo_destroy(cr_pixmap);
            gdk_threads_leave();

            cairo_surface_destroy(cst);

            currently_drawing = 0;

        }
    }
}

gboolean timer_exe(GtkWidget * simuiW) {
    static int first_time = 1;
    
    int drawing_status = g_atomic_int_get(&currently_drawing);

    /*drawing thread must be created if first time*/
    static pthread_t thread_info;
    if(first_time == 1){
        int  iret;
        iret = pthread_create( &thread_info, NULL, do_draw, NULL);
    }

    /*if no drawing is happening, send SIGALRM to update pixmap*/
    if(drawing_status == 0){
        pthread_kill(thread_info, SIGALRM);
    }

    /*send an artificial expose event to the simuiW*/
    int width, height;
    gdk_drawable_get_size(pixmap, &width, &height);
    gtk_widget_queue_draw_area(simuiW, 0, 0, width, height);

    first_time = 0;
    return TRUE;
}


int main (int argc, char *argv[]) {
    /*block SIGALRM in the main thread*/
    sigset_t sigset;
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGALRM);
    pthread_sigmask(SIG_BLOCK, &sigset, NULL);

	/*initialize functions to make gtk thread-aware(not thread safe)*/
	/*the conditional makes sure that g_thread_init() has not been previously called in the code; which is not strictly necessary*/
    if (!g_thread_supported ()){ g_thread_init(NULL); }
    gdk_threads_init();
    gdk_threads_enter();

	GladeXML* simXML; /*XML for the main simulation UI*/

    gtk_init(&argc, &argv);

	simXML = glade_xml_new("simui.glade", NULL, NULL);
	glade_xml_signal_autoconnect(simXML);

	/*initialize immiediately visible simuiW*/
/*    GtkWidget *simuiW = gtk_simuiW_new(GTK_simuiW_TOPLEVEL);*/
/*    gtk_simuiW_set_default_size(GTK_simuiW(simuiW), WDT, HGT);*/
/*    gtk_simuiW_set_title(GTK_simuiW(simuiW), "ARXSim Dev UI");*/
	  /*connect basic signals to simuiW*/
/*    g_signal_connect(G_OBJECT(simuiW), "destroy", G_CALLBACK(gtk_main_quit), NULL);*/
/*    g_signal_connect(G_OBJECT(simuiW), "expose_event", G_CALLBACK(win_expose), NULL);*/
/*    g_signal_connect(G_OBJECT(simuiW), "configure_event", G_CALLBACK(win_config), NULL);*/

    /*shows the simuiW before defining pixmap so that color depth can be infered from simuiW*/
/*    gtk_widget_show_all(simuiW);*/

    /*initializes the global pixmap with previously defined dimensions and color depth infered from its parent simuiW*/
    pixmap = gdk_pixmap_new(simuiW->window, WDT, HGT, -1);
    /*disable automatic GTK widget painting and double buffering*/
    gtk_widget_set_app_paintable(simuiW, TRUE);
    gtk_widget_set_double_buffered(simuiW, FALSE);

	/*timer that will call timer_exe (which paints on simuiW) 30 times a second if resources are available to do so*/
    (void)g_timeout_add(33, (GSourceFunc)timer_exe, simuiW);

    gtk_main();
    
    /*terminate threads when all other GTK processes have been terminated*/
    gdk_threads_leave();

    return 0;
}
